﻿using System;
using System.Runtime.InteropServices;

namespace DotNetDetour
{
    public enum Protection
    {
        PAGE_NOACCESS = 0x01,
        PAGE_READONLY = 0x02,
        PAGE_READWRITE = 0x04,
        PAGE_WRITECOPY = 0x08,
        PAGE_EXECUTE = 0x10,
        PAGE_EXECUTE_READ = 0x20,
        PAGE_EXECUTE_READWRITE = 0x40,
        PAGE_EXECUTE_WRITECOPY = 0x80,
        PAGE_GUARD = 0x100,
        PAGE_NOCACHE = 0x200,
        PAGE_WRITECOMBINE = 0x400
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SYSTEM_INFO
    {
        public uint dwOemId;
        public uint dwPageSize;
        public IntPtr lpMinimumApplicationAddress;
        public IntPtr lpMaximumApplicationAddress;
        public IntPtr dwActiveProcessorMask;
        public uint dwNumberOfProcessors;
        public uint dwProcessorType;
        public uint dwAllocationGranularity;
        public ushort dwProcessorLevel;
        public ushort dwProcessorRevision;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MEMORY_BASIC_INFORMATION32
    {
        public int BaseAddress;
        public int AllocationBase;
        public int AllocationProtect;
        public int RegionSize;
        public int State;
        public int Protect;
        public int lType;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MEMORY_BASIC_INFORMATION64
    {
        public IntPtr BaseAddress;
        public IntPtr AllocationBase;
        public int AllocationProtect;
        private int __alignment1;
        public ulong RegionSize;
        public int State;
        public int Protect;
        public int lType;
        private int __alignment2;
    }

    public class NativeAPI
    {
        [DllImport("kernel32.dll")]
        public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetModuleHandleW")]
        public static extern IntPtr GetModuleHandle(string lpModuleName);

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "LoadLibraryW")]
        public static extern IntPtr LoadLibrary(string lpLibFileName);

        [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
        public static extern void CopyMemory(
           IntPtr Destination,
           IntPtr Source,
           int Length);

        [DllImport("kernel32.dll")]
        public static extern int VirtualFree(IntPtr lpAddress, uint dwSize, int dwFreeType);

        [DllImport("kernel32.dll")]
        public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, int flAllocationType, Protection flProtect);

        [DllImport("kernel32.dll")]
        public static extern int VirtualQuery(IntPtr lpAddress, out MEMORY_BASIC_INFORMATION32 mbi, int dwLength);

        [DllImport("kernel32.dll")]
        public static extern int VirtualQuery(IntPtr lpAddress, out MEMORY_BASIC_INFORMATION64 mbi, int dwLength);

        public static dynamic VirtualQuery(IntPtr lpAddress)
        {
            if (IntPtr.Size == 4)
            {
                VirtualQuery(lpAddress, out MEMORY_BASIC_INFORMATION32 tmp,
                  Marshal.SizeOf(typeof(MEMORY_BASIC_INFORMATION32)));
                return tmp;
            }
            else
            {
                VirtualQuery(lpAddress, out MEMORY_BASIC_INFORMATION64 tmp,
                  Marshal.SizeOf(typeof(MEMORY_BASIC_INFORMATION64)));
                return tmp;
            }
        }

        public const int MEM_COMMIT = 0x1000;
        public const int MEM_FREE = 0x10000;
        public const int MEM_RESERVE = 0x2000;
        public const int MEM_IMAGE = 0x1000000;
        public const int MEM_MAPPED = 0x40000;
        public const int MEM_PRIVATE = 0x20000;
        public const int MEM_RELEASE = 0x00008000;
        public const int MEM_DECOMMIT = 0x00004000;

        [DllImport("kernel32")]
        public static extern void GetSystemInfo(out SYSTEM_INFO pSI);

        [DllImport("kernel32")]
        public static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, Protection flNewProtect, out uint lpflOldProtect);
    }
}